package com.zengjianjun.oss.platform.tencent;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson2.JSON;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.zengjianjun.oss.enums.OssPlatformEnum;
import com.zengjianjun.oss.exception.OssException;
import com.zengjianjun.oss.platform.AbstractOssClient;
import com.zengjianjun.oss.platform.param.FileListParam;
import com.zengjianjun.oss.platform.result.FileListResult;
import com.zengjianjun.oss.properties.TencentCosProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zengjianjun
 */
@Slf4j
public class TencentCosClient extends AbstractOssClient {

    private final COSClient ossClient;

    public TencentCosClient(TencentCosProperties properties) {
        try {
            COSCredentials cred = new BasicCOSCredentials(properties.getAccessKeyId(), properties.getAccessKeySecret());
            // 2 设置 bucket 的地域, COS 地域的简称请参见 https://cloud.tencent.com/document/product/436/6224
            // clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题 Java SDK 部分。
            Region region = new Region(properties.getRegion());
            ClientConfig clientConfig = new ClientConfig(region);
            // 这里建议设置使用 https 协议
            // 从 5.6.54 版本开始，默认使用了 https
            clientConfig.setHttpProtocol(HttpProtocol.https);
            // 3 生成 cos 客户端。
            this.ossClient = new COSClient(cred, clientConfig);
            log.info("init {} oss success......", get().name());
        } catch (Exception e) {
            log.error("init {} oss error：", get().name(), e);
            throw OssException.build(e);
        }
    }

    @Override
    protected OssPlatformEnum get() {
        return OssPlatformEnum.tencent;
    }

    @Override
    public List<FileListResult> getFileList(FileListParam param) {
        log.info("[{}] 获取文件文件列表开始：param = {}", get().name(), JSON.toJSONString(param));
        long startTime = System.currentTimeMillis();
        ListObjectsRequest listObjects = new ListObjectsRequest();
        // 设置 bucket 名称
        listObjects.setBucketName(param.getBucketName());
        // prefix 表示列出的 object 的 key 以 prefix 开始
        listObjects.setPrefix(param.getPrefix());
        // deliter 表示分隔符, 设置为/表示列出当前目录下的 object, 设置为空表示列出所有的 object
        listObjects.setDelimiter(param.getDelimiter());
        // 设置最大遍历出多少个对象, 一次 listobject 最大支持1000
        listObjects.setMaxKeys(param.getMaxKeys());
        ObjectListing result = this.ossClient.listObjects(listObjects);
        // object summary 表示所有列出的 object 列表
        List<COSObjectSummary> cosObjectSummaries = result.getObjectSummaries();
        List<FileListResult> list = new ArrayList<>(cosObjectSummaries.size());
        for (COSObjectSummary data : cosObjectSummaries) {
            if (StringUtils.isNotEmpty(param.getEndFinish()) && data.getKey().compareTo(param.getEndFinish()) >= 0) {
                break;
            }
            list.add(FileListResult.builder()
                    .bucketName(param.getBucketName())
                    .key(data.getKey())
                    .size(data.getSize())
                    .build());
        }
        log.info("[{}] 获取文件文件列表结束：耗时{}ms", get().name(), System.currentTimeMillis() - startTime);
        return list;
    }

    @Override
    public FileListResult getFile(String bucketName, String fileKey) {
        // 获取全部元数据可以使用：ObjectMetadata metadata = ossClient.getObjectMetadata(bucketName, objectName);
        log.info("[{}] 获取文件原数据：bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        long startTime = System.currentTimeMillis();
        // 查询对象元数据
        ObjectMetadata objectMetadata = this.ossClient.getObjectMetadata(bucketName, fileKey);
        long endTime = System.currentTimeMillis();
        log.info("[{}] 获取文件原数据：result = {}, 耗时{}ms", get().name(), JSON.toJSONString(objectMetadata), endTime - startTime);
        return FileListResult.builder()
                .bucketName(bucketName)
                .size(objectMetadata.getContentLength())
                .key(fileKey)
                .build();
    }

    @Override
    public List<FileListResult> getFullFileList(FileListParam param) {
        // 不提供返回所有的文件列表的接口
        throw OssException.build("不提供返回所有的文件列表的接口");
    }

    @Override
    public void download(String bucketName, String fileKey, String filePath) {
        // 下载Object到本地文件，并保存到指定的本地路径中。
        log.info("[{}] 下载文件到本地: bucketName = {}, fileKey = {}, filePath = {}", get().name(), bucketName, fileKey, filePath);
        long startTime = System.currentTimeMillis();
        // 若父目录不存在创建父目录
        File file = new File(filePath);
        if (!file.getParentFile().exists()) {
            FileUtil.mkdir(file.getParentFile());
        }
        // 若文件存在，删除原文件
        if (file.exists()) {
            FileUtil.del(file);
        }
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileKey);
        this.ossClient.getObject(getObjectRequest, file);
        long endTime = System.currentTimeMillis();
        log.info("[{}] 下载文件到本地: 耗时{}ms", get().name(), endTime - startTime);
    }

    @Override
    public void upload(String bucketName, String fileKey, String filePath) {
        this.upload(bucketName, fileKey, new File(filePath));
    }

    @Override
    public void upload(String bucketName, String fileKey, File file) {
        long startTime = System.currentTimeMillis();
        log.info("[{}] 文件上传: bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        // fileKey: 指定文件上传到 COS 上的路径，即对象键。例如对象键为 folder/picture.jpg，则表示将文件 picture.jpg 上传到 folder 路径下
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileKey, file);
        this.ossClient.putObject(putObjectRequest);
        long endTime = System.currentTimeMillis();
        log.info("[{}] 文件上传结束: 耗时{}ms", get().name(), endTime - startTime);
    }

    @Override
    public void delete(String bucketName, String fileKey) {
        long startTime = System.currentTimeMillis();
        log.info("[{}] 文件删除: bucketName = {}, fileKey = {}", get().name(), bucketName, fileKey);
        this.ossClient.deleteObject(bucketName, fileKey);
        long endTime = System.currentTimeMillis();
        log.debug("[{}] 文件删除结束: 耗时{}ms", get().name(), endTime - startTime);
    }
}
